using System;
using System.Collections.Generic;
using System.Text;
using System.Timers;
using SpoonCore2.Client;

namespace SpoonCore2.Tools
{
	public class ReplicationManager<T>
	{
		private static readonly SpoonIdentifier Step = new SpoonIdentifier();

		public enum CheckStatus { Ok, Missing, OutOfDate, Timeout }
		public delegate void CheckCompleteHandle(SpoonIdentifier replicationId, T item, CheckStatus status);
		public delegate void CheckBeginHandle(SpoonIdentifier item, CheckCompleteHandle completeHandle);
		public delegate void RefreshHandle(SpoonIdentifier replicationId, T item);

		private readonly Timer _timer = new Timer(30 * 1000);
		private readonly SortedList<SpoonIdentifier, ReplicationItem> _data = new SortedList<SpoonIdentifier, ReplicationItem>();
		private readonly RefreshHandle _refreshHandle;
		private readonly CheckBeginHandle _checkHandle;
		private int _degree = 3;

		private readonly SortedList<int, OffsetItem> _offsetList = new SortedList<int, OffsetItem>();

		public double Interval
		{
			get { return _timer.Interval; }
			set { _timer.Interval = value; }
		}

		public int Degree
		{
			get { return _degree; }
			set
			{
				_degree = value;
				RefreshOffsetList();
			}
		}

		private void RefreshOffsetList()
		{
			int max = _degree / 2;
			TimeSpan baseInterval = new TimeSpan(0, 0, 10);
			_offsetList.Add(0, new OffsetItem(SpoonIdentifier.Empty, baseInterval));
			for (int x = 1; x <= max; x++)
			{
				_offsetList.Add(x, new OffsetItem(Step * x, baseInterval));
				_offsetList.Add(-x, new OffsetItem(SpoonIdentifier.Empty - Step * x, baseInterval));
			}
		}

		public ReplicationManager(CheckBeginHandle checkHandle, RefreshHandle refreshHandle)
		{
			_checkHandle = checkHandle;
			_refreshHandle = refreshHandle;

			_timer.Elapsed += _timer_Elapsed;
			_timer.Start();

			RefreshOffsetList();
		}

		private void _timer_Elapsed(object sender, ElapsedEventArgs e)
		{
			foreach (KeyValuePair<SpoonIdentifier, ReplicationItem> pair in _data)
			{
				if (pair.Value.Updated + _offsetList[pair.Value.Degree].Interval >= DateTime.Now)
				{
					foreach (OffsetItem valuePair in _offsetList.Values)
					{
						_checkHandle(pair.Key + valuePair.Step, CheckComplete);
					}
					pair.Value.Updated = DateTime.Now;
				}
			}
		}

		private void CheckComplete(SpoonIdentifier id, T item, CheckStatus status)
		{
			switch (status)
			{
				case CheckStatus.Missing:
				case CheckStatus.OutOfDate:
				case CheckStatus.Timeout:
					_refreshHandle(id, item);
					break;
			}
		}

		public void Register(SpoonIdentifier id, T item)
		{
			_data.Add(id, new ReplicationItem(0, item));
		}

		private class ReplicationItem
		{
			public readonly int Degree;
			public readonly T Item;
			public DateTime Updated = DateTime.Now;

			public ReplicationItem(int degree, T item)
			{
				Degree = degree;
				Item = item;
			}
		}

		private class OffsetItem
		{
			public readonly SpoonIdentifier Step;
			public readonly TimeSpan Interval;

			public OffsetItem(SpoonIdentifier step, TimeSpan interval)
			{
				Step = step;
				Interval = interval;
			}
		}
	}
}